﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Savannah.Core
{
    /// <summary>
    /// Savannah osztály ami felépíti magát a szavannát
    /// </summary>
    public static class Savannah
    {
        internal delegate void DayPassedEventHandler(DayPassedEventArgs args);

        internal static event DayPassedEventHandler DayPassed;


        /// <summary>
        /// Szavanna vízszintes hossza
        /// </summary>
        static int savannahXLength;
        /// <summary>
        /// Szavanna függőleges hossza
        /// </summary>
        static int savannahYLength;

        /// <summary>
        /// Cellák a szavannában(pl.:ezen vannak az állatok)
        /// </summary>
        static internal Cell[,] savannah;

        static Random rnd = new Random();

        /// <summary>
        /// A tesztek számára visszaadja a cellákat
        /// </summary>
        /// <returns></returns>
        static internal Cell[,] GetAllCells()
        {
            return savannah;
        }


        /// <summary>
        /// Iniciálja a szavvannát
        /// </summary>
        /// <param name="data">Ez a data példány alapján inicializál</param>
       static internal void Init(Data data)
       {
            savannahXLength = data.SavannahSizeX;
            savannahYLength = data.SavannahSizeY;

            savannah = new Cell[savannahXLength, savannahYLength];

            Random rnd = new Random();
            int x = rnd.Next(0, savannahXLength);
            int y = rnd.Next(0, savannahYLength);


            for (int i = 0; i < savannahXLength; i++)
            {
                for (int j = 0; j < savannahYLength; j++)
                {
                    savannah[i, j] = new Cell(FieldType.land, null, new List<AnimalBase>(), i, j);
                }
            }


            while (data.PlantNumber > 0)
            {
                while (savannah[x, y] != null && savannah[x, y].Plantp != null)
                {
                    x = rnd.Next(0, savannahXLength);
                    y = rnd.Next(0, savannahYLength);
                }

                savannah[x, y] = new Cell(FieldType.land, new Plant(rnd.Next(1, 7)), new List<AnimalBase>(), x, y);
                data.PlantNumber--;
            }


            bool sex = false;
            while (data.GetCountOfAnimals() > 0)
            {
                while (savannah[x, y].Animals.Count > 4 || savannah[x, y].GetAdultNumber() > 2 || data.GetMaxOfAnimal() is Predator && ContainsVegetarian(savannah[x,y]))
                {
                    x = new Random().Next(0, savannahXLength);
                    y = new Random().Next(0, savannahYLength);
                }

                AnimalBase selectedLuckyAnimal = data.GetMaxOfAnimal();
                selectedLuckyAnimal.xCoordinate = x;
                selectedLuckyAnimal.yCoordinate = y;
                selectedLuckyAnimal.SexEnumProperty = sex == false ? Sex.male : Sex.female;

                savannah[x, y].AddAnimal(selectedLuckyAnimal);

                DecreaseCountOfAnimalType(ref data);
                sex = !sex;
            }

        }

        static bool ContainsVegetarian(Cell cell)
        {
            foreach (AnimalBase item in cell.Animals)
            {
                if (item is Vegetarian)
                {
                    return false;
                }
            }

            return false;
        }


        /// <summary>
        /// Csökkenti annak az állatfajtánaknak a számát a Data példányban, ami meg van adva
        /// </summary>
        /// <param name="animal">ennek az állatnak csökkenti a számát</param>
        /// <param name="parameters">ebben a Data pélgányban</param>
        static void DecreaseCountOfAnimalType(ref Data parameters)     
        {
            AnimalBase animal = parameters.GetMaxOfAnimal();

            if (animal is Gazelle)
            {
                --parameters.GazelleNumber;
                return;
            }
            if (animal is Giraffe)
            {
                --parameters.GiraffeNumber;
                return;
            }
            if (animal is Crocodile)
            {
                --parameters.CrocodileNumber;
                return;
            }
            if (animal is Hyena)
            {
                --parameters.HyenaNumber;
                return;
            }
            if (animal is Lion)
            {
                --parameters.LionNumber;
                return;
            }
        }

        /// <summary>
        /// Elindítja az életet a szavannán, nem áll le amíg van rajta állat
        /// </summary>
        static public void Run(Data data)
        {
            Init(data);

            DayPassed?.Invoke(new DayPassedEventArgs(buildArgs()));

            while (GetAllAnimal().Count > 0)
            {
                PassOneDay();
                DayPassed?.Invoke(new DayPassedEventArgs(buildArgs()));
            }
        }


        static Cell[,] buildArgs()
        {
            Cell[,] cells = new Cell[savannahXLength, savannahYLength];
            AnimalBase almostAClone;

            for (int i = 0; i < savannahXLength; i++)
            {
                for (int j = 0; j < savannahYLength; j++)
                {
                    cells[i, j] = new Cell(FieldType.land, savannah[i, j].Plantp != null ? new Plant(savannah[i, j].Plantp.PlantSize) : null, new List<AnimalBase>(), i, j);
                    foreach (AnimalBase item in savannah[i,j].Animals)
                    {                        
                        Type type = item.GetType();
                        ConstructorInfo constructor = type.GetConstructor(new Type[0]);

                        almostAClone = (AnimalBase)constructor.Invoke(new object[0]);
                        almostAClone.xCoordinate = item.xCoordinate;
                        almostAClone.yCoordinate = item.yCoordinate;
                        almostAClone.CurrentAge = item.CurrentAge;

                        cells[i, j].AddAnimal(almostAClone);
                    }
                }
            }

            return cells;
        }


        /// <summary>
        /// Lefuttat egy "napot" a szavannán
        /// </summary>
        static internal void PassOneDay()
        {
            lock (savannah)
            {
                DropDeadAnimals();
            }
            List<AnimalBase> animals = GetAllAnimal();
            int index;

            foreach (Cell item in savannah)
            {
                if (item.Plantp != null)
                {
                    item.Plantp.Grow();
                }
            }

            while (animals.Count > 0)
            {
                index = rnd.Next(0, animals.Count);
                Cell[,] paramMap = CollectCells(animals[index]);
                animals[index].Live(ref paramMap);
                ChangeCells(paramMap);

                animals.RemoveAt(index);
            }
        }

        /// <summary>
        /// A döglött állatokat kiüríti a cellákról
        /// </summary>
        static internal void DropDeadAnimals()
        {
            foreach (Cell currentCell in savannah)
            {
                int index = 0;
                while (index<currentCell.Animals.Count)
                {
                    if (currentCell.Animals[index].CurrentAge > currentCell.Animals[index].MaximumAge || currentCell.Animals[index].HungerLevel <= 0)
                    {
                        currentCell.Animals.Remove(currentCell.Animals[index]);
                    }
                    else
                    {
                        ++index;
                    }
                }
            }
        }


        /// <summary>
        /// Kiválogatja a szavanna cellái közül, hogy az állat melyik cellákra léphet(figyelembe veszi hogy vízi v szárazföldi a cella)
        /// </summary>
        /// <param name="pAnimal">Ennek az állat körüli cellákból válogat(x és y koordinátája kell hogy legyen)</param>
        /// <returns></returns>
        static Cell[,] CollectCells(AnimalBase pAnimal)
        {
            int startX, startY, endX, endY;

   /*         if (pAnimal.HungerLevel <= 2 || pAnimal.ThirstyLevel <= 3)
            {
                startX = pAnimal.xCoordinate - pAnimal.RangeOfMove < 0 ? 0 : pAnimal.xCoordinate - pAnimal.RangeOfMove;
                startY = pAnimal.yCoordinate - pAnimal.RangeOfMove < 0 ? 0 : pAnimal.yCoordinate - pAnimal.RangeOfMove;
                endX = pAnimal.xCoordinate + pAnimal.RangeOfMove > savannahXLength - 1 ? savannahXLength : pAnimal.xCoordinate + pAnimal.RangeOfMove;
                endY = pAnimal.yCoordinate + pAnimal.RangeOfMove > savannahYLength - 1 ? savannahYLength : pAnimal.yCoordinate + pAnimal.RangeOfMove;
            }
            else
            {         */
                startX = pAnimal.xCoordinate - pAnimal.RangeOfView < 0 ? 0 : pAnimal.xCoordinate - pAnimal.RangeOfView;
                startY = pAnimal.yCoordinate - pAnimal.RangeOfView < 0 ? 0 : pAnimal.yCoordinate - pAnimal.RangeOfView;
                endX = pAnimal.xCoordinate + pAnimal.RangeOfView > savannahXLength - 1 ? savannahXLength : pAnimal.xCoordinate + pAnimal.RangeOfView;
                endY = pAnimal.yCoordinate + pAnimal.RangeOfView > savannahYLength - 1 ? savannahYLength : pAnimal.yCoordinate + pAnimal.RangeOfView;
    //        }

            Cell[,] Collected = new Cell[endX-startX, endY - startY];

            int lineindex = 0, columnindex = 0;
            for (int i = startX; i < endX; ++i)
            {
                for (int j = startY; j < endY; ++j)
                {
                    if ((int)pAnimal.AnimalTypeEnumProperty == (int)savannah[columnindex, lineindex].CellType)
                    {
                        Collected[columnindex, lineindex] = savannah[i, j];
                    }
                    ++lineindex;
                }
                ++columnindex;
                lineindex = 0;
            }

            return Collected;
        }

        /// <summary>
        /// A megváltoztatott cellákat lecseréli(hogy PassOneDay hatást fejtsen ki, elengedhetetlen)
        /// </summary>
        /// <param name="pCells">ezeket a cellákat cseréljük(!!!x,y koordináta!!!)</param>
        static void ChangeCells(Cell[,] pCells)
        {
            foreach (Cell actCell in pCells)
            {
                savannah[actCell.CoordX, actCell.CoordY] = actCell;
            }
        }

        /// <summary>
        /// Listát készít a szavanna állatairól
        /// </summary>
        /// <returns>AnimalBase típusú lista</returns>
        static List<AnimalBase> GetAllAnimal()
        {
            List<AnimalBase> animals = new List<AnimalBase>();

            foreach (Cell item in savannah)
            {
                foreach (AnimalBase animal in item.GetAnimalList())
                {
                    animals.Add(animal);
                }
            }

            return animals;
        }
    }
}